<!DOCTYPE html>



  


<html class="theme-next gemini use-motion" lang="zh-Hans">
<head><meta name="generator" content="Hexo 3.8.0">
  <meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, 
maximum-scale=1">
<script src="//cdn.bootcss.com/pace/1.0.2/pace.min.js"></script>
<meta name="theme-color" content="#222">

<script>
    (function () {
        if ('') {
            if (prompt('请输入文章密码') !== '') {
                alert('密码错误！');
                if (history.length === 1) {
                    location.replace("http://xxxxxxx.xxx"); // 这里替换成你的首页
                } else {
                    history.back();
                }
            }
        }
    })();
</script>


  
  
    
    
  <script src="/lib/pace/pace.min.js?v=1.0.2"></script>
  <link href="/lib/pace/pace-theme-minimal.min.css?v=1.0.2" rel="stylesheet">







<meta http-equiv="Cache-Control" content="no-transform">
<meta http-equiv="Cache-Control" content="no-siteapp">
















  
  
  <link href="/lib/fancybox/source/jquery.fancybox.css?v=2.1.5" rel="stylesheet" type="text/css">







<link href="/lib/font-awesome/css/font-awesome.min.css?v=4.6.2" rel="stylesheet" type="text/css">

<link href="/css/main.css?v=5.1.4" rel="stylesheet" type="text/css">


  <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png?v=5.1.4">


  <link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32-next.png?v=5.1.4">


  <link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16-next.png?v=5.1.4">


  <link rel="mask-icon" href="/images/logo.svg?v=5.1.4" color="#222">





  <meta name="keywords" content="mysql,">










<meta name="description" content="高性能MySQL读书笔记（二）  索引补充 MySQL服务器逻辑架构 MySQL锁 事务 隔离级别 并发级别和MVCC MySQL死锁级别   我需要好多的钱= =。 索引补充B+Tree是在B-Tree基础上的一种优化，使其更适合实现外存储索引结构，B+Tree相对于B-Tree有几点不同：  非叶子节点只存储键值信息。  所有叶子节点之间都有一个链指针。  数据记录都存放在叶子节点中。">
<meta name="keywords" content="mysql">
<meta property="og:type" content="article">
<meta property="og:title" content="高性能MySQL读书笔记（二）">
<meta property="og:url" content="http://sabistian.cn/2019/11/03/高性能MySQL读书笔记(二)/index.html">
<meta property="og:site_name" content="Sabistian    |      Dear">
<meta property="og:description" content="高性能MySQL读书笔记（二）  索引补充 MySQL服务器逻辑架构 MySQL锁 事务 隔离级别 并发级别和MVCC MySQL死锁级别   我需要好多的钱= =。 索引补充B+Tree是在B-Tree基础上的一种优化，使其更适合实现外存储索引结构，B+Tree相对于B-Tree有几点不同：  非叶子节点只存储键值信息。  所有叶子节点之间都有一个链指针。  数据记录都存放在叶子节点中。">
<meta property="og:locale" content="zh-Hans">
<meta property="og:image" content="https://sabistianblogimg.oss-cn-beijing.aliyuncs.com/sql3.png">
<meta property="og:image" content="https://sabistianblogimg.oss-cn-beijing.aliyuncs.com/sql4.png">
<meta property="og:image" content="https://sabistianblogimg.oss-cn-beijing.aliyuncs.com/sql5.png">
<meta property="og:image" content="https://sabistianblogimg.oss-cn-beijing.aliyuncs.com/sql6.png">
<meta property="og:updated_time" content="2020-03-03T01:41:53.599Z">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="高性能MySQL读书笔记（二）">
<meta name="twitter:description" content="高性能MySQL读书笔记（二）  索引补充 MySQL服务器逻辑架构 MySQL锁 事务 隔离级别 并发级别和MVCC MySQL死锁级别   我需要好多的钱= =。 索引补充B+Tree是在B-Tree基础上的一种优化，使其更适合实现外存储索引结构，B+Tree相对于B-Tree有几点不同：  非叶子节点只存储键值信息。  所有叶子节点之间都有一个链指针。  数据记录都存放在叶子节点中。">
<meta name="twitter:image" content="https://sabistianblogimg.oss-cn-beijing.aliyuncs.com/sql3.png">



<script type="text/javascript" id="hexo.configurations">
  var NexT = window.NexT || {};
  var CONFIG = {
    root: '/',
    scheme: 'Gemini',
    version: '5.1.4',
    sidebar: {"position":"left","display":"post","offset":12,"b2t":false,"scrollpercent":false,"onmobile":false},
    fancybox: true,
    tabs: true,
    motion: {"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}},
    duoshuo: {
      userId: '0',
      author: '博主'
    },
    algolia: {
      applicationID: '',
      apiKey: '',
      indexName: '',
      hits: {"per_page":10},
      labels: {"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}
    }
  };
</script>



  <link rel="canonical" href="http://sabistian.cn/2019/11/03/高性能MySQL读书笔记(二)/">





  <title>高性能MySQL读书笔记（二） | Sabistian    |      Dear</title>
  








</head>

<body itemscope itemtype="http://schema.org/WebPage" lang="zh-Hans">

  
  
    
  

  <div class="container sidebar-position-left page-post-detail">
    <div class="headband"></div>

    <header id="header" class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-wrapper">
  <div class="site-meta ">
    

    <div class="custom-logo-site-title">
      <a href="/" class="brand" rel="start">
        <span class="logo-line-before"><i></i></span>
        <span class="site-title">Sabistian    |      Dear</span>
        <span class="logo-line-after"><i></i></span>
      </a>
    </div>
      
        <p class="site-subtitle"></p>
      
  </div>

  <div class="site-nav-toggle">
    <button>
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
    </button>
  </div>
</div>

<nav class="site-nav">
  

  
    <ul id="menu" class="menu">
      
        
        <li class="menu-item menu-item-home">
          <a href="/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-home"></i> <br>
            
            首页
          </a>
        </li>
      
        
        <li class="menu-item menu-item-about">
          <a href="/about/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-user"></i> <br>
            
            关于
          </a>
        </li>
      
        
        <li class="menu-item menu-item-tags">
          <a href="/tags/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-tags"></i> <br>
            
            标签
          </a>
        </li>
      
        
        <li class="menu-item menu-item-categories">
          <a href="/categories/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-th"></i> <br>
            
            分类
          </a>
        </li>
      
        
        <li class="menu-item menu-item-archives">
          <a href="/archives/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-archive"></i> <br>
            
            归档
          </a>
        </li>
      

      
    </ul>
  

  
</nav>



 </div>
    </header>

    <main id="main" class="main">
      <div class="main-inner">
        <div class="content-wrap">
          <div id="content" class="content">
            

  <div id="posts" class="posts-expand">
    

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="http://sabistian.cn/2019/11/03/高性能MySQL读书笔记(二)/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="邱灵雨">
      <meta itemprop="description" content>
      <meta itemprop="image" content="/images/headicon.png">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Sabistian    |      Dear">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">高性能MySQL读书笔记（二）</h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2019-11-03T20:13:09+08:00">
                2019-11-03
              </time>
            

            

            
          </span>

          
            <span class="post-category">
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">分类于</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/categories/mysql/" itemprop="url" rel="index">
                    <span itemprop="name">mysql</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
          

          
          

          

          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        <h1 id="高性能MySQL读书笔记（二）"><a href="#高性能MySQL读书笔记（二）" class="headerlink" title="高性能MySQL读书笔记（二）"></a>高性能MySQL读书笔记（二）</h1><blockquote>
<ul>
<li>索引补充</li>
<li>MySQL服务器逻辑架构</li>
<li>MySQL锁</li>
<li>事务</li>
<li>隔离级别</li>
<li>并发级别和MVCC</li>
<li>MySQL死锁级别</li>
</ul>
</blockquote>
<p>我需要好多的钱= =。</p>
<h2 id="索引补充"><a href="#索引补充" class="headerlink" title="索引补充"></a>索引补充</h2><p>B+Tree是在B-Tree基础上的一种优化，使其更适合实现外存储索引结构，B+Tree相对于B-Tree有几点不同：</p>
<ul>
<li><p>非叶子节点只存储键值信息。</p>
</li>
<li><p>所有叶子节点之间都有一个链指针。</p>
</li>
<li><p>数据记录都存放在叶子节点中。</p>
<a id="more"></a> 

</li>
</ul>
<p>数据库中的B+Tree索引可以分为<strong>聚集索引</strong>（clustered index）和<strong>辅助索引</strong>（secondary index）。<br>上面的B+Tree示例图在数据库中的实现即为聚集索引，聚集索引的B+Tree中的叶子节点存放的是整张表的行记录数据。<br>辅助索引与聚集索引的区别在于辅助索引的叶子节点并不包含行记录的全部数据，而是存储相应行数据的聚集索引键，即主键。<br>当通过辅助索引来查询数据时，InnoDB存储引擎会遍历辅助索引找到主键，然后再通过主键在聚集索引中找到完整的行记录数据。</p>
<h3 id="hash索引"><a href="#hash索引" class="headerlink" title="hash索引"></a>hash索引</h3><p>而哈希索引的示意图则是这样的：<br><img src="https://sabistianblogimg.oss-cn-beijing.aliyuncs.com/sql3.png" alt="sql3"></p>
<p>Hash 索引结构的特殊性，其检索效率非常高，索引的检索可以一次定位，不像B-Tree 索引需要从根节点到枝节点，最后才能访问到页节点这样多次的IO访问，所以 Hash 索引的查询效率要远高于 B-Tree 索引。</p>
<p>既然 Hash 索引的效率要比 B-Tree 高很多，为什么大家不都用 Hash 索引而还要使用 B-Tree 索引呢？</p>
<p>任何事物都是有两面性的，Hash 索引也一样，虽然 Hash 索引效率高，但是 Hash 索引本身由于其特殊性也带来了很多限制和弊端，主要有以下这些。</p>
<ul>
<li>Hash 索引仅仅能满足”=”,”IN”和”&lt;&gt;”查询，不能使用范围查询。</li>
<li>Hash 索引无法被用来避免数据的排序操作。</li>
<li>Hash 索引不能利用部分索引键查询。</li>
<li>对于组合索引，Hash 索引在计算 Hash 值的时候是组合索引键合并后再一起计算 Hash 值，而不是单独计算 Hash 值，所以通过组合索引的前面一个或几个索引键进行查询的时候，Hash 索引也无法被利用。</li>
<li>Hash 索引在任何时候都不能避免表扫描。<br>Hash 索引是将索引键通过 Hash 运算之后，将 Hash运算结果的 Hash 值和所对应的行指针信息存放于一个 Hash 表中，由于不同索引键存在相同 Hash 值，所以即使取满足某个 Hash 键值的数据的记录条数，也无法从 Hash 索引中直接完成查询，还是要通过访问表中的实际数据进行相应的比较，并得到相应的结果。</li>
<li>Hash 索引遇到大量Hash值相等的情况后性能并不一定就会比B-Tree索引高。</li>
</ul>
<p>简单地说，哈希索引就是采用一定的哈希算法，把键值换算成新的哈希值，检索时不需要类似B+树那样从根节点到叶子节点逐级查找，只需一次哈希算法即可立刻定位到相应的位置，速度非常快。</p>
<p>B+树索引和哈希索引的明显区别是：</p>
<blockquote>
<p>1).如果是等值查询，那么哈希索引明显有绝对优势，因为只需要经过一次算法即可找到相应的键值；当然了，这个前提是，键值都是唯一的。如果键值不是唯一的，就需要先找到该键所在位置，然后再根据链表往后扫描，直到找到相应的数据；</p>
</blockquote>
<blockquote>
<p>2).从示意图中也能看到，如果是范围查询检索，这时候哈希索引就毫无用武之地了，因为原先是有序的键值，经过哈希算法后，有可能变成不连续的了，就没办法再利用索引完成范围查询检索；</p>
</blockquote>
<blockquote>
<p>3).同理，哈希索引也没办法利用索引完成排序，以及like ‘xxx%’ 这样的部分模糊查询（这种部分模糊查询，其实本质上也是范围查询）；</p>
</blockquote>
<blockquote>
<p>4).哈希索引也不支持多列联合索引的最左匹配规则；</p>
</blockquote>
<blockquote>
<p>5).B+树索引的关键字检索效率比较平均，不像B树那样波动幅度大，在有大量重复键值情况下，哈希索引的效率也是极低的，因为存在所谓的哈希碰撞问题。</p>
</blockquote>
<p>在MySQL中，只有HEAP/MEMORY引擎表才能显式支持哈希索引（NDB也支持，但这个不常用），InnoDB引擎的自适应哈希索引（adaptive hash index）不在此列，因为这不是创建索引时可指定的。</p>
<p>还需要注意到：HEAP/MEMORY引擎表在mysql实例重启后，数据会丢失。</p>
<p>通常，B+树索引结构适用于绝大多数场景，像下面这种场景用哈希索引才更有优势：</p>
<p>在HEAP表中，如果存储的数据重复度很低（也就是说基数很大），对该列数据以等值查询为主，没有范围查询、没有排序的时候，特别适合采用哈希索引<br>例如这种SQL：<br>SELECT … FROM t WHERE C1 = ?; — 仅等值查询<br>在大多数场景下，都会有范围查询、排序、分组等查询特征，用B+树索引就可以了。</p>
<p>Hash索引特点<br>hash索引仅仅用于比较是否相等的操作，不支持范围查询，如用&lt;查询一个范围的值。<br>在ordery by中使用hash索引页不会提速，对月hash索引的命中必须是一个完整的索引。</p>
<p>mysql 主键默认索引<br>mysql 表中如果是复合主键的话，那系统会遵循左对齐原则，即如复合主键 a 和 b字段和c字段…，<br>默认建的主键索引是abc即主键全有，把所有主键全部建立在一个单独的主键索引中。<br>那只有条件中有a的话这个索引才会命中，条件中如果不带a，只带b或其他的都不会命中索引，即主键索引都不起作用。<br>由于a字段是在b字段前，索引遵循左对齐规则，只会把a默认作为比较对象，条件中只有带a才会命中，<br>查询中如 where a=?   或   where a=?  and  b=?   这2种 查询条件中只要有a就会用到主键索引，即a、ab、abc、a…<br>只要有a就会用到索引，而 b、bc、bd、bcd、cd、… 条件中只要没有a的都没有用到索引，<br>查询的话就会很慢，索引要么在条件中把a带上，要么把条件中的b或者c等 也单独 建个索引。</p>
<h2 id="MySQL服务器逻辑架构"><a href="#MySQL服务器逻辑架构" class="headerlink" title="MySQL服务器逻辑架构"></a>MySQL服务器逻辑架构</h2><p><img src="https://sabistianblogimg.oss-cn-beijing.aliyuncs.com/sql4.png" alt="sql4"></p>
<p>每个连接都会在mysql服务端产生一个线程（内部通过线程池管理线程）</p>
<p>比如一个select语句进入，mysql首先会在查询缓存中查找是否缓存了这个select的结果集，如果没有则继续执行 解析、优化、执行的过程；否则会之间从缓存中获取结果集。</p>
<h2 id="MySQL锁"><a href="#MySQL锁" class="headerlink" title="MySQL锁"></a>MySQL锁</h2><h3 id="Shared-and-Exclusive-Locks（共享锁与排他锁）"><a href="#Shared-and-Exclusive-Locks（共享锁与排他锁）" class="headerlink" title="Shared and Exclusive Locks（共享锁与排他锁）"></a>Shared and Exclusive Locks（共享锁与排他锁）</h3><p>它们都是标准的行级锁。</p>
<ul>
<li>共享锁 ：也称为读锁，读锁允许多个连接可以同一时刻并发的读取同一资源,互不干扰；</li>
<li>排他锁 ：也称为写锁，一个写锁会阻塞其他的写锁或读锁，保证同一时刻只有一个连接可以写入数据，同时防止其他用户对这个数据的读写。</li>
</ul>
<p>注意：所谓共享锁、排他锁其实均是锁机制本身的策略，通过这两种策略对锁做了区分。</p>
<h3 id="Intention-Locks（意向锁）"><a href="#Intention-Locks（意向锁）" class="headerlink" title="Intention Locks（意向锁）"></a>Intention Locks（意向锁）</h3><p>InnoDB支持多粒度锁(锁粒度可分为行锁和表锁)，允许行锁和表锁共存。例如，一个语句，例如LOCK TABLES…WRITE接受指定表上的独占锁。为了实现多粒度级别的锁定，InnoDB使用了意向</p>
<ul>
<li>意向锁：表级别的锁。先提前声明一个意向，并获取表级别的意向锁（IS或IX），如果获取成功，则稍后将要或正在(才被允许)，对该表的某些行加锁(S或X)了。（除了 LOCK TABLES … WRITE,很少用,略)锁。<br>举例来说：<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">SELECT … LOCK IN SHARE MODE，要获取IS锁；An intention shared lock (IS) 共享</span><br><span class="line">SELECT … FOR UPDATE ，要获取IX锁；An intention exclusive lock (IX) i   排他</span><br></pre></td></tr></table></figure>

</li>
</ul>
<p>意向锁协议<br>在事务能够获取表中的行上的共享锁之前，它必须首先获取表上的IS锁或更强的锁。<br>在事务能够获取表中的行上的独占锁之前，它必须首先获取表上的IX锁。</p>
<p>前文说了，意向锁实现的背景是多粒度锁的并存场景。如下兼容性的汇总：<br>可以体会到，意向锁是比X\S更弱的锁，存在一种预判的意义！先获取更弱的IX\IS锁，如果获取失败就不必要再花费跟大开销获取更强的X\S锁。</p>
<p>个人理解</p>
<p>①在mysql中有表锁<br>LOCK TABLE my_tabl_name READ;<br>用读锁锁表，会阻塞其他事务修改表数据。<br>LOCK TABLE my_table_name WRITE;<br>用写锁锁表，会阻塞其他事务读和写。</p>
<p>②Innodb引擎又支持行锁，行锁分为<br>共享锁，一个事务对一行的共享只读锁。<br>排它锁，一个事务对一行的排他读写锁。</p>
<p>③这两中类型的锁共存的问题<br>考虑这个例子：<br>事务A锁住了表中的一行，让这一行只能读，不能写。之后，事务B申请整个表的写锁。<br>如果事务B申请成功，那么理论上它就能修改表中的任意一行，这与A持有的行锁是冲突的。<br>数据库需要避免这种冲突，就是说要让B的申请被阻塞，直到A释放了行锁。<br>数据库要怎么判断这个冲突呢？<br>step1：判断表是否已被其他事务用表锁锁表<br>step2：判断表中的每一行是否已被行锁锁住。<br>注意step2，这样的判断方法效率实在不高，因为需要遍历整个表。<br>于是就有了意向锁。<br>在意向锁存在的情况下，事务A必须先申请表的意向共享锁，成功后再申请一行的行锁。<br>在意向锁存在的情况下，上面的判断可以改成<br>step1：不变<br>step2：发现表上有意向共享锁，说明表中有些行被共享行锁锁住了，因此，事务B申请表的写锁会被阻塞。<br>注意：申请意向锁的动作是数据库完成的，就是说，事务A申请一行的行锁的时候，<br>数据库会自动先开始申请表的意向锁，不需要我们程序员使用代码来申请。</p>
<h3 id="Record-Locks-索引行锁"><a href="#Record-Locks-索引行锁" class="headerlink" title="Record Locks (索引行锁)"></a>Record Locks (索引行锁)</h3><p>这个很好理解，结合查询过程来看，不加索引就会锁表<br>可以用show engine innodb status 命令查看</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">RECORD LOCKS space id 58 page no 3 n bits 72 index `PRIMARY` of table `test`.`t` </span><br><span class="line">trx id 10078 lock_mode X locks rec but not gap</span><br><span class="line">Record lock, heap no 2 PHYSICAL RECORD: n_fields 3; compact format; info bits 0</span><br><span class="line">0: len 4; hex 8000000a; asc     ;;</span><br><span class="line">1: len 6; hex 00000000274f; asc     O;;</span><br><span class="line">2: len 7; hex b60000019d0110; asc        ;;</span><br></pre></td></tr></table></figure>

<h3 id="Gap-locks（间隙锁）"><a href="#Gap-locks（间隙锁）" class="headerlink" title="Gap locks（间隙锁）"></a>Gap locks（间隙锁）</h3><p>锁定索引记录之间的间隙([2])，或者锁定一个索引记录之前的间隙([1])，或者锁定一个索引记录之后的间隙([3])。<br>示例：如图[1]、[2]、[3]部分。一般作用于我们的范围查询&gt; 、&lt; 、between…</p>
<p>例如， SELECT userId FROM t1 WHERE userId BETWEEN 1 and 4 FOR UPDATE;<br>阻止其他事务将值3插入到列userId中。因为该范围内所有现有值之间的间隙都是锁定的。</p>
<p>对于使用唯一索引来搜索唯一行的语句 select a from ，不产生间隙锁定。(不包含组合唯一索引，也就是说gapLock不作用于单例唯一索引）</p>
<p><img src="https://sabistianblogimg.oss-cn-beijing.aliyuncs.com/sql5.png" alt="sql5"></p>
<h3 id="next-key-lock"><a href="#next-key-lock" class="headerlink" title="next-key lock"></a>next-key lock</h3><p>比如 存在一个查询匹配b=3的行(b上有个非唯一索引)，那么所谓NextLock就是：<br>在b=3的行加了RecordLock 并且使用 GapLock锁定了b=3之前（“之前”：索引排序）的所有行记录。<br>mysql查询时执行 行级锁策略，会对扫描过程中匹配的行进行加锁（X 或S），也就是加Record Lock，<br>同时会对这个记录之前的所有行加GapLock锁。</p>
<h3 id="Insert-Intention-Locks（插入意向锁）"><a href="#Insert-Intention-Locks（插入意向锁）" class="headerlink" title="Insert Intention Locks（插入意向锁）"></a>Insert Intention Locks（插入意向锁）</h3><p>一个insert intention lock 是一种发生在 insert 插入语句时的gap lock 间隙锁，锁定插入行之前的所有行。</p>
<p>这个锁以这样一种方式表明插入的意图，如果插入到同一索引间隙中的多个事务没有插入到该间隙中的相同位置，则它们不需要等待对方。</p>
<p>假设存在值为4和7的索引记录。尝试分别插入值为5和6的独立事务，在获得所插入行上的独占锁之前，每个事务使用insert intention lock 锁定4和7之间的间隙，但不会阻塞彼此，因为这些行不冲突。</p>
<p>示例</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; CREATE TABLE child (id int(11) NOT NULL, PRIMARY KEY(id)) ENGINE=InnoDB;</span><br><span class="line">mysql&gt; INSERT INTO child (id) values (90),(102);</span><br><span class="line">##事务一</span><br><span class="line">mysql&gt; START TRANSACTION;</span><br><span class="line">mysql&gt; SELECT * FROM child WHERE id &gt; 100 FOR UPDATE;</span><br><span class="line">+-----+</span><br><span class="line">| id  |</span><br><span class="line">+-----+</span><br><span class="line">| 102 |</span><br><span class="line">+-----+</span><br><span class="line"></span><br><span class="line">##事务二</span><br><span class="line"></span><br><span class="line">mysql&gt; START TRANSACTION;</span><br><span class="line">mysql&gt; INSERT INTO child (id) VALUES (101);</span><br><span class="line">##失败，已被锁定</span><br><span class="line"></span><br><span class="line">mysql&gt; SHOW ENGINE INNODB STATUS</span><br><span class="line"> </span><br><span class="line">RECORD LOCKS space id 31 page no 3 n bits 72 index `PRIMARY` of table `test`.`child`</span><br><span class="line">trx id 8731 lock_mode X locks gap before rec insert intention waiting</span><br><span class="line">Record lock, heap no 3 PHYSICAL RECORD: n_fields 3; compact format; info bits 0</span><br><span class="line"> 0: len 4; hex 80000066; asc    f;;</span><br><span class="line"> 1: len 6; hex 000000002215; asc     &quot; ;;</span><br><span class="line"> 2: len 7; hex 9000000172011c; asc     r  ;;...</span><br></pre></td></tr></table></figure>

<h3 id="AUTO-INC-Locks"><a href="#AUTO-INC-Locks" class="headerlink" title="AUTO-INC Locks"></a>AUTO-INC Locks</h3><p>AUTO-INC锁是一种特殊的表级锁，产生于这样的场景：事务插入(inserting into )到具有AUTO_INCREMENT列的表中。<br>在最简单的情况下，如果一个事务正在向表中插入值，那么其他任何事务必须等待向该表中插入它们自己的值，<br>以便由第一个事务插入的行接收连续的主键值。</p>
<h2 id="事务"><a href="#事务" class="headerlink" title="事务"></a>事务</h2><p>事务就是一组原子性的sql，或者说一个独立的工作单元。<br>事务就是说，要么mysql引擎会全部执行这一组sql语句，要么全部都不执行（比如其中一条语句失败的话）。<br>事务的ACID</p>
<p>比如，tim要给bill转账100块钱：<br>1.检查tim的账户余额是否大于100块；<br>2.tim的账户减少100块；<br>3.bill的账户增加100块；</p>
<blockquote>
<p>A:atomiciy原子性<br>一个事务必须保证其中的操作要么全部执行，要么全部回滚，不可能存在只执行了一部分这种情况出现。</p>
</blockquote>
<blockquote>
<p>C:consistency一致性<br>数据必须保证从一种一致性的状态转换为另一种一致性状态。<br>比如一个事务中执行了第二步时系统崩溃了，数据也不会出现bill的账户少了100块，但是tim的账户没变的情况。要么维持原装（全部回滚），要么bill少了100块同时tim多了100块，只有这两种一致性状态的</p>
</blockquote>
<blockquote>
<p>I:isolation隔离性<br>在一个事务未执行完毕时，通常会保证其他Session 无法看到这个事务的执行结果</p>
</blockquote>
<blockquote>
<p>D:durability持久性<br>事务一旦commit，则数据就会保存下来，即使提交完之后系统崩溃，数据也不会丢失。</p>
</blockquote>
<h2 id="隔离级别"><a href="#隔离级别" class="headerlink" title="隔离级别"></a>隔离级别</h2><p><img src="https://sabistianblogimg.oss-cn-beijing.aliyuncs.com/sql6.png" alt="sql6"></p>
<p>一个事务中多次执行统一读SQL,返回结果一样。这个隔离级别解决了脏读的问题，幻读问题。这里指的是innodb的rr级别.<br>innodb中使用next-key锁对”当前读”进行加锁，锁住行以及可能产生幻读的插入位置，阻止新的数据插入产生幻行。</p>
<p>串行化 最强的隔离级别，通过给事务中每次读取的行加锁，写加写锁，保证不产生幻读问题，但是会导致大量超时以及锁争用问题。</p>
<p><strong>TIPS</strong> 业务需求需要串行化的事务时,优先从逻辑上来处理，如应避免先清空整表的某字段，在逐一修改某字段.可在修改字段的第一次操作时，做清空处理。</p>
<h2 id="并发级别和MVCC"><a href="#并发级别和MVCC" class="headerlink" title="并发级别和MVCC"></a>并发级别和MVCC</h2><p>MVCC(multiple-version-concurrency-control）是个行级锁的变种，它在普通读情况下避免了加锁操作，因此开销更低。<br>虽然实现不同，但通常都是实现非阻塞读，对于写操作只锁定必要的行。一致性读 （就是读取快照）<br>select * from table …;</p>
<p>当前读(就是读取实际的持久化的数据)<br>特殊的读操作，插入/更新/删除操作，属于当前读，处理的都是当前的数据，需要加锁。<br>select * from table where ? lock in share mode;<br>select * from table where ? for update;<br>insert;<br>update;<br>delete;<br>注意：select … from where… （没有额外加锁后缀）使用MVCC，保证了读快照(mysql称为consistent read)，<br>所谓一致性读或者读快照就是读取当前事务开始之前的数据快照，在这个事务开始之后的更新不会被读到。<br>对于加锁读SELECT with FOR UPDATE(排他锁) or LOCK IN SHARE MODE(共享锁)、<br>update、delete语句，要考虑是否是唯一索引的等值查询。</p>
<p>INNODB的MVCC通常是通过在每行数据后边保存两个隐藏的列来实现(其实是三列，第三列是用于事务回滚，此处略去)，<br>一个保存了行的创建版本号，另一个保存了行的更新版本号（上一次被更新数据的版本号）<br>这个版本号是每个事务的版本号，递增的。这样保证了innodb对读操作不需要加锁也能保证正确读取数据。</p>
<h3 id="MVCC-select无锁操作-与-维护版本号"><a href="#MVCC-select无锁操作-与-维护版本号" class="headerlink" title="MVCC select无锁操作 与 维护版本号"></a>MVCC select无锁操作 与 维护版本号</h3><p>下边在mysql默认的Repeatable Read隔离级别下，具体看看MVCC操作：<br> <strong>Select（快照读，所谓读快照就是读取当前事务之前的数据）</strong>：</p>
<ul>
<li><p>InnoDB只select查找版本号早于当前版本号的数据行，这样保证了读取的数据要么是在这个事务开始之前就已经commit了的（早于当前版本号），要么是在这个事务自身中执行创建操作的数据（等于当前版本号）。</p>
</li>
<li><p>查找行的更新版本号要么未定义，要么大于当前的版本号(为了保证事务可以读到老数据)，这样保证了事务读取到在当前事务开始之后未被更新的数据。</p>
</li>
</ul>
<p>注意： 这里的select不能有for update、lock in share 语句。总之要只返回满足以下条件的行数据，达到了快照读的效果：<br>(行创建版本号&lt; =当前版本号 &amp;&amp; (行更新版本号==null or 行更新版本号&gt;当前版本号 ) )</p>
<p><strong>Insert</strong><br>InnoDB为这个事务中新插入的行，保存当前事务版本号的行作为行的行创建版本号。</p>
<p><strong>Delete</strong><br>InnoDB为每一个删除的行保存当前事务版本号，作为行的删除标记。</p>
<p><strong>Update</strong><br>将存在两条数据，保持当前版本号作为更新后的数据的新增版本号，同时保存当前版本号作为老数据行的更新版本号。</p>
<p>当前版本号—写—&gt;新数据行创建版本号 &amp;&amp; 当前版本号—写—&gt;老数据更新版本号();</p>
<h3 id="脏读-vs-幻读-vs-不可重复读"><a href="#脏读-vs-幻读-vs-不可重复读" class="headerlink" title="脏读 vs 幻读 vs 不可重复读"></a>脏读 vs 幻读 vs 不可重复读</h3><p><strong>脏读</strong>：一事务未提交的中间状态的更新数据被其他会话读取到。</p>
<p>当一个事务正在访问数据，并且对数据进行了修改，而这种修改还没有 提交到数据库中(commit未执行)，这时，另外会话也访问这个数据，因为这个数据是还没有提交，那么另外一个会话读到的这个数据是脏数据，依据脏数据所做的操作也可能是不正确的。</p>
<p><strong>不可重复读</strong>：简单来说就是在一个事务中读取的数据可能产生变化，ReadCommitted也称为不可重复读。</p>
<p>在同一事务中，多次读取同一数据返回的结果有所不同。换句话说就是，后续读取可以读到另一会话事务已提交的更新数据。相反，“可重复读”在同一事务中多次读取数据时，能够保证所读数据一样，也就是，后续读取不能读到另一会话事务已提交的更新数据。</p>
<p><strong>幻读</strong>：会话T1事务中执行一次查询，然后会话T2新插入一行记录，这行记录恰好可以满足T1所使用的查询的条件。然后T1又使用相同 的查询再次对表进行检索，但是此时却看到了事务T2刚才插入的新行。这个新行就称为“幻像”，因为对T1来说这一行就像突然 出现的一样。</p>
<p>insert 主键冲突就说明幻读出现</p>
<h3 id="如何保证rr级别绝对不产生幻读？"><a href="#如何保证rr级别绝对不产生幻读？" class="headerlink" title="如何保证rr级别绝对不产生幻读？"></a>如何保证rr级别绝对不产生幻读？</h3><p>在使用的select …where语句中加入 for update(排他锁) 或者 lock in share mode(共享锁)语句来实现。<br>其实就是锁住了可能造成幻读的数据，阻止数据的写入操作。</p>
<p>其实是因为数据的写入操作(insert 、update)需要先获取写锁，由于可能产生幻读的部分，已经获取到了某种锁，所以要在另外一个会话中获取写锁的前提是当前会话中释放所有因加锁语句产生的锁。</p>
<h3 id="从另一个角度看锁：显式锁-隐式锁"><a href="#从另一个角度看锁：显式锁-隐式锁" class="headerlink" title="从另一个角度看锁：显式锁 隐式锁"></a>从另一个角度看锁：显式锁 隐式锁</h3><p>隐式锁:我们上文说的锁都属于不需要额外语句加锁的隐式锁。<br>显示锁：<br>SELECT … LOCK IN SHARE MODE(加共享锁);<br>SELECT … FOR UPDATE(加排他锁);<br>详情上文已经说过。</p>
<h3 id="查看锁情况"><a href="#查看锁情况" class="headerlink" title="查看锁情况"></a>查看锁情况</h3><p>通过如下sql可以查看等待锁的情况</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">select * from low_game_schema.innodb_trx where trx_state=&quot;lock wait&quot;;</span><br><span class="line">或</span><br><span class="line">show engine innodb status;</span><br></pre></td></tr></table></figure>

<h2 id="MySQL死锁级别"><a href="#MySQL死锁级别" class="headerlink" title="MySQL死锁级别"></a>MySQL死锁级别</h2><p>死锁，就是产生了循环等待链条，我等待你的资源，你却等待我的资源，我们都相互等待，谁也不释放自己占有的资源，导致无线等待下去。<br>比如：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">//Session A</span><br><span class="line">START TRANSACTION;</span><br><span class="line">UPDATE account SET p_money=p_money-100 WHERE p_name=&quot;tim&quot;;</span><br><span class="line">UPDATE account SET p_money=p_money+100 WHERE p_name=&quot;bill&quot;;</span><br><span class="line">COMMIT;</span><br><span class="line">//Thread B</span><br><span class="line">START TRANSACTION;</span><br><span class="line">UPDATE account SET p_money=p_money+100 WHERE p_name=&quot;bill&quot;;</span><br><span class="line">UPDATE account SET p_money=p_money-100 WHERE p_name=&quot;tim&quot;;</span><br><span class="line">COMMIT;</span><br></pre></td></tr></table></figure>

<p>innodb_lock_wait_timeout 等待锁超时回滚事务：<br>直观方法是在两个事务相互等待时，当一个等待时间超过设置的某一阀值时，对其中一个事务进行回滚，另一个事务就能继续执行。这种方法简单有效，在innodb中，参数innodb_lock_wait_timeout用来设置超时时间。</p>
<p>wait-for graph算法来主动进行死锁检测：<br>innodb还提供了wait-for graph算法来主动进行死锁检测，每当加锁请求无法立即满足需要并进入等待时，wait-for graph算法都会被触发。</p>
<h3 id="如何尽可能避免死锁"><a href="#如何尽可能避免死锁" class="headerlink" title="如何尽可能避免死锁"></a>如何尽可能避免死锁</h3><blockquote>
<ul>
<li>1) 以固定的顺序访问表和行。比如两个更新数据的事务，事务A 更新数据的顺序 为1，2；事务B更新数据的顺序为2，1。这样更可能会造成死锁。</li>
</ul>
</blockquote>
<blockquote>
<ul>
<li>2） 大事务拆小。大事务更倾向于死锁，如果业务允许，将大事务拆小。</li>
</ul>
</blockquote>
<blockquote>
<ul>
<li>3） 在同一个事务中，尽可能做到一次锁定所需要的所有资源，减少死锁概率。</li>
</ul>
</blockquote>
<blockquote>
<ul>
<li>4） 降低隔离级别。如果业务允许，将隔离级别调低也是较好的选择，比如将隔离级别从RR调整为RC，可以避免掉很多因为gap锁造成的死锁。（我司mysql规范做法）</li>
</ul>
</blockquote>
<blockquote>
<ul>
<li>5） 为表添加合理的索引。可以看到如果不走索引将会为表的每一行记录添加上锁，死锁的概率大大增大。</li>
</ul>
</blockquote>
<p>之前干活的时候 很多查询过程都会采取下面这样的方式去做处理</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">declare var type default 0;</span><br><span class="line">select col into var from table; </span><br><span class="line">if found_rows() != 0 </span><br><span class="line"> ...</span><br><span class="line">endif</span><br></pre></td></tr></table></figure>

<p>某天一个25仔说这个var也没声明的必要呀，直接</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">select col from table;</span><br><span class="line">if found_rows() != 0 </span><br><span class="line"> ...</span><br><span class="line"> select ...</span><br><span class="line">endif</span><br></pre></td></tr></table></figure>

<p>结果发现返回的结果不是自己想像这样的逻辑 = =;</p>
<p>从存储过程中返回数据：可通过三种方法将数据从过程返回到调用程序：结果集、输出参数和返回代码。 </p>
<p>使用结果集返回数据，如果存储过程的正文中包含 SELECT 语句（而不是 SELECT …INTO 或 INSERT…SELECT），则 SELECT 语句指定的行将直接发送到客户端。<br>对于较大的结果集，在将结果集完全发送到客户端之前，存储过程不会继续执行下一个语句。<br>对于较小的结果集，存储过程将对结果进行后台处理以便返回给客户端，并继续执行。 </p>
<p>如果在执行存储过程期间运行多个此类 SELECT 语句，则会将多个结果集发送到客户端。 此行为也适用于嵌套 TSQL 批处理、嵌套存储过程和顶级 TSQL 批处理。</p>
<p>可见如果不是SELECT ….INTO 那就返回第一个结果集喽，当然也可以用输出参数去返回</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line">-- ----------------------------</span><br><span class="line">-- Procedure structure for `sp_lobby_create_user_name`</span><br><span class="line">-- ----------------------------</span><br><span class="line">DROP PROCEDURE IF EXISTS `sp_lobby_create_user_name`;</span><br><span class="line">DELIMITER ;;</span><br><span class="line">CREATE PROCEDURE `sp_lobby_create_user_name`(in `_in_user_guid` bigint, IN `_in_user_name` varchar(30))</span><br><span class="line">BEGIN</span><br><span class="line">	DECLARE _user_name VARCHAR(30);</span><br><span class="line">	DECLARE _result int default 0;</span><br><span class="line"></span><br><span class="line">START TRANSACTION;</span><br><span class="line">	SELECT user_name INTO _user_name FROM t_user_name WHERE user_name = `_in_user_name`;</span><br><span class="line">	IF FOUND_ROWS() = 0 THEN</span><br><span class="line">		INSERT INTO t_user_name (user_name) VALUES( `_in_user_name`);</span><br><span class="line">		Update user_info set user_name = `_in_user_name` where user_guid = `_in_user_guid`;</span><br><span class="line">		set _user_name = _in_user_name;</span><br><span class="line">		set _result = 1;   </span><br><span class="line">	END IF;</span><br><span class="line">COMMIT;</span><br><span class="line">	SELECT _user_name, _result;</span><br><span class="line">END</span><br><span class="line">;;</span><br><span class="line">DELIMITER ;</span><br></pre></td></tr></table></figure>

<p>附一个开发时的查询过程模板。</p>
<h2 id="相关资料"><a href="#相关资料" class="headerlink" title="相关资料"></a>相关资料</h2><p><a href="https://dev.mysql.com/doc/refman/5.7/en/innodb-locking.html#innodb-shared-exclusive-locks" target="_blank" rel="noopener">参考资料</a><br><a href="https://blog.csdn.net/lemon89/article/details/51477497" target="_blank" rel="noopener">参考博客</a>；</p>

      
    </div>
    
    
    

    

    
      <div>
        <div style="padding: 10px 0; margin: 20px auto; width: 90%; text-align: center;">
  <div></div>
  <button id="rewardButton" disable="enable" onclick="var qr = document.getElementById('QR'); if (qr.style.display === 'none') {qr.style.display='block';} else {qr.style.display='none'}">
    <span>打赏</span>
  </button>
  <div id="QR" style="display: none;">

    
      <div id="wechat" style="display: inline-block">
        <img id="wechat_qr" src="/images/wechatpay.jpg" alt="邱灵雨 微信支付">
        <p>微信支付</p>
      </div>
    

    
      <div id="alipay" style="display: inline-block">
        <img id="alipay_qr" src="/images/alipay.jpg" alt="邱灵雨 支付宝">
        <p>支付宝</p>
      </div>
    

    

  </div>
</div>

      </div>
    

    
      <div>
        
      <ul class="post-copyright">
         <li class="post-copyright-link">
          <strong>本文作者：</strong>
          <a href="/" title="欢迎访问 邱灵雨 的个人博客">邱灵雨</a>
        </li>

        <li class="post-copyright-link">
          <strong>本文链接：</strong>
          <a href="http://sabistian.cn/2019/11/03/高性能MySQL读书笔记(二)/" title="高性能MySQL读书笔记（二）">http://sabistian.cn/2019/11/03/高性能MySQL读书笔记(二)/</a>
        </li>

        <li class="post-copyright-license">
          <strong>版权声明： </strong>
          本文由 邱灵雨 原创，采用 <a href="https://creativecommons.org/licenses/by-nc-nd/4.0/deed.zh" rel="license" target="_blank">保留署名-非商业性使用-禁止演绎 4.0-国际许可协议</a> <br>转载请保留以上声明信息！
        </li>
      </ul>
<div style="text-align:center;color: #ccc;font-size:14px;">
------ 本文结束 ------</div>
      </div>
    

    <footer class="post-footer">
      
      
        <div class="post-tags">
          
            <a href="/tags/mysql/" rel="tag"><i class="fa fa-tag"></i> mysql</a>
          
        </div>
      

      
      
      

      
        <div class="post-nav">
          <div class="post-nav-next post-nav-item">
            
              <a href="/2019/11/01/高性能MySQL读书笔记(一)/" rel="next" title="高性能MySQL读书笔记（一）">
                <i class="fa fa-chevron-left"></i> 高性能MySQL读书笔记（一）
              </a>
            
          </div>

          <span class="post-nav-divider"></span>

          <div class="post-nav-prev post-nav-item">
            
              <a href="/2019/11/17/高性能MySQL读书笔记(三)/" rel="prev" title="高性能MySQL读书笔记（三）">
                高性能MySQL读书笔记（三） <i class="fa fa-chevron-right"></i>
              </a>
            
          </div>
        </div>
      

      
      
    </footer>
  </div>
  
  
  
  </article>



    <div class="post-spread">
      
    </div>
  </div>


          </div>
          


          

  



        </div>
        
          
  
  <div class="sidebar-toggle">
    <div class="sidebar-toggle-line-wrap">
      <span class="sidebar-toggle-line sidebar-toggle-line-first"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-middle"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-last"></span>
    </div>
  </div>

  <aside id="sidebar" class="sidebar">
    
    <div class="sidebar-inner">

      

      
        <ul class="sidebar-nav motion-element">
          <li class="sidebar-nav-toc sidebar-nav-active" data-target="post-toc-wrap">
            文章目录
          </li>
          <li class="sidebar-nav-overview" data-target="site-overview-wrap">
            站点概览
          </li>
        </ul>
      

      <section class="site-overview-wrap sidebar-panel">
        <div class="site-overview">
          <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
            
              <img class="site-author-image" itemprop="image" src="/images/headicon.png" alt="邱灵雨">
            
              <p class="site-author-name" itemprop="name">邱灵雨</p>
              <p class="site-description motion-element" itemprop="description">灵雨既零</p>
          </div>

          <nav class="site-state motion-element">

            
              <div class="site-state-item site-state-posts">
              
                <a href="/archives/">
              
                  <span class="site-state-item-count">21</span>
                  <span class="site-state-item-name">日志</span>
                </a>
              </div>
            

            
              
              
              <div class="site-state-item site-state-categories">
                <a href="/categories/index.html">
                  <span class="site-state-item-count">7</span>
                  <span class="site-state-item-name">分类</span>
                </a>
              </div>
            

            
              
              
              <div class="site-state-item site-state-tags">
                <a href="/tags/index.html">
                  <span class="site-state-item-count">20</span>
                  <span class="site-state-item-name">标签</span>
                </a>
              </div>
            

          </nav>

          

          
            <div class="links-of-author motion-element">
                
                  <span class="links-of-author-item">
                    <a href="https://github.com/sabistian" target="_blank" title="GitHub">
                      
                        <i class="fa fa-fw fa-github"></i>GitHub</a>
                  </span>
                
                  <span class="links-of-author-item">
                    <a href="mailto:sebistian@163.com" target="_blank" title="Mail">
                      
                        <i class="fa fa-fw fa-envelope"></i>Mail</a>
                  </span>
                
                  <span class="links-of-author-item">
                    <a href="https://weibo.com/5975979965/profile?rightmod=1&wvr=6&mod=personinfo" target="_blank" title="Weibo">
                      
                        <i class="fa fa-fw fa-weibo"></i>Weibo</a>
                  </span>
                
                  <span class="links-of-author-item">
                    <a href="https://www.zhihu.com/people/kuai-shen-l/activities" target="_blank" title="ZhiHu">
                      
                        <i class="fa fa-fw fa-globe"></i>ZhiHu</a>
                  </span>
                
            </div>
          

          
          

          
          

          

        </div>
      </section>

      
      <!--noindex-->
        <section class="post-toc-wrap motion-element sidebar-panel sidebar-panel-active">
          <div class="post-toc">

            
              
            

            
              <div class="post-toc-content"><ol class="nav"><li class="nav-item nav-level-1"><a class="nav-link" href="#高性能MySQL读书笔记（二）"><span class="nav-number">1.</span> <span class="nav-text">高性能MySQL读书笔记（二）</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#索引补充"><span class="nav-number">1.1.</span> <span class="nav-text">索引补充</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#hash索引"><span class="nav-number">1.1.1.</span> <span class="nav-text">hash索引</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#MySQL服务器逻辑架构"><span class="nav-number">1.2.</span> <span class="nav-text">MySQL服务器逻辑架构</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#MySQL锁"><span class="nav-number">1.3.</span> <span class="nav-text">MySQL锁</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#Shared-and-Exclusive-Locks（共享锁与排他锁）"><span class="nav-number">1.3.1.</span> <span class="nav-text">Shared and Exclusive Locks（共享锁与排他锁）</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Intention-Locks（意向锁）"><span class="nav-number">1.3.2.</span> <span class="nav-text">Intention Locks（意向锁）</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Record-Locks-索引行锁"><span class="nav-number">1.3.3.</span> <span class="nav-text">Record Locks (索引行锁)</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Gap-locks（间隙锁）"><span class="nav-number">1.3.4.</span> <span class="nav-text">Gap locks（间隙锁）</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#next-key-lock"><span class="nav-number">1.3.5.</span> <span class="nav-text">next-key lock</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Insert-Intention-Locks（插入意向锁）"><span class="nav-number">1.3.6.</span> <span class="nav-text">Insert Intention Locks（插入意向锁）</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#AUTO-INC-Locks"><span class="nav-number">1.3.7.</span> <span class="nav-text">AUTO-INC Locks</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#事务"><span class="nav-number">1.4.</span> <span class="nav-text">事务</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#隔离级别"><span class="nav-number">1.5.</span> <span class="nav-text">隔离级别</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#并发级别和MVCC"><span class="nav-number">1.6.</span> <span class="nav-text">并发级别和MVCC</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#MVCC-select无锁操作-与-维护版本号"><span class="nav-number">1.6.1.</span> <span class="nav-text">MVCC select无锁操作 与 维护版本号</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#脏读-vs-幻读-vs-不可重复读"><span class="nav-number">1.6.2.</span> <span class="nav-text">脏读 vs 幻读 vs 不可重复读</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#如何保证rr级别绝对不产生幻读？"><span class="nav-number">1.6.3.</span> <span class="nav-text">如何保证rr级别绝对不产生幻读？</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#从另一个角度看锁：显式锁-隐式锁"><span class="nav-number">1.6.4.</span> <span class="nav-text">从另一个角度看锁：显式锁 隐式锁</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#查看锁情况"><span class="nav-number">1.6.5.</span> <span class="nav-text">查看锁情况</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#MySQL死锁级别"><span class="nav-number">1.7.</span> <span class="nav-text">MySQL死锁级别</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#如何尽可能避免死锁"><span class="nav-number">1.7.1.</span> <span class="nav-text">如何尽可能避免死锁</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#相关资料"><span class="nav-number">1.8.</span> <span class="nav-text">相关资料</span></a></li></ol></li></ol></div>
            

          </div>
        </section>
      <!--/noindex-->
      

      

    </div>
  </aside>


        
      </div>
    </main>

    <footer id="footer" class="footer">
      <div class="footer-inner">
        <script async src="https://busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>

<div class="copyright">&copy; <span itemprop="copyrightYear">2020</span>
  <span class="with-love">
    <i class="fa fa-user"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">邱灵雨</span>

  
</div>

<div class="powered-by">
<i class="fa fa-user-md"></i><span id="busuanzi_container_site_uv">
  本站访客数:<span id="busuanzi_value_site_uv"></span>
</span>
</div>


  <div class="powered-by">由 <a class="theme-link" target="_blank" href="https://hexo.io">Hexo</a> 强力驱动</div>



  <span class="post-meta-divider">|</span>



  <div class="theme-info">主题 &mdash; <a class="theme-link" target="_blank" href="https://github.com/iissnan/hexo-theme-next">NexT.Gemini</a> v5.1.4</div>




        







        
      </div>
    </footer>

    
      <div class="back-to-top">
        <i class="fa fa-arrow-up"></i>
        
      </div>
    

    

  </div>

  

<script type="text/javascript">
  if (Object.prototype.toString.call(window.Promise) !== '[object Function]') {
    window.Promise = null;
  }
</script>









  












  
  
    <script type="text/javascript" src="/lib/jquery/index.js?v=2.1.3"></script>
  

  
  
    <script type="text/javascript" src="/lib/fastclick/lib/fastclick.min.js?v=1.0.6"></script>
  

  
  
    <script type="text/javascript" src="/lib/jquery_lazyload/jquery.lazyload.js?v=1.9.7"></script>
  

  
  
    <script type="text/javascript" src="/lib/velocity/velocity.min.js?v=1.2.1"></script>
  

  
  
    <script type="text/javascript" src="/lib/velocity/velocity.ui.min.js?v=1.2.1"></script>
  

  
  
    <script type="text/javascript" src="/lib/fancybox/source/jquery.fancybox.pack.js?v=2.1.5"></script>
  


  


  <script type="text/javascript" src="/js/src/utils.js?v=5.1.4"></script>

  <script type="text/javascript" src="/js/src/motion.js?v=5.1.4"></script>



  
  


  <script type="text/javascript" src="/js/src/affix.js?v=5.1.4"></script>

  <script type="text/javascript" src="/js/src/schemes/pisces.js?v=5.1.4"></script>



  
  <script type="text/javascript" src="/js/src/scrollspy.js?v=5.1.4"></script>
<script type="text/javascript" src="/js/src/post-details.js?v=5.1.4"></script>



  


  <script type="text/javascript" src="/js/src/bootstrap.js?v=5.1.4"></script>



  


  




	





  





  












  





  

  

  

  
  

  

  

  

</body>
</html>
